/////////////////////////////////////////////////////////////////////////////
// Name:        variantpropertyobj.cpp
// Purpose:     A variant type supporting a set of objects each with properties
// Author:      Julian Smart
// Modified by:
// Created:     2006-05-11
// RCS-ID:      $$
// Copyright:   (c) Julian Smart
// Licence:     wxWindows Licence
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"

#include "wx/module.h"
#include "wx/datstrm.h"

#include "variantutils/variantimage.h"
#include "variantutils/variantpropertyobj.h"
#include "variantutils/varianthandlers.h"

/*
 * wxVariantObjectSetData
 */

IMPLEMENT_DYNAMIC_CLASS(wxVariantObjectSetData, wxClonableVariantData)

wxVariantObjectSetData::wxVariantObjectSetData(const wxVariantObjectSetData& data)
{
    wxASSERT_MSG( (GetType() == wxT("objectset")), wxT("wxVariantObjectSetData::wxVariantObjectSetData: Can't copy to this type of data") );

    if (data.GetType() != wxT("objectset"))
        return;

    for (wxNode* node = data.m_objects.GetFirst(); node; node = node->GetNext())
    {
        wxVariant* obj = wxDynamicCast(node->GetData(), wxVariant);
        if (obj)
            m_objects.Append(new wxVariant(*obj));
    }
}

wxVariantObjectSetData::wxVariantObjectSetData()
{
}

wxVariantObjectSetData::~wxVariantObjectSetData()
{
    Clear();
}

void wxVariantObjectSetData::Clear()
{
    for (wxNode* node = m_objects.GetFirst(); node; node = node->GetNext())
    {
        wxVariant* obj = wxDynamicCast(node->GetData(), wxVariant);
        delete obj;
    }
    m_objects.Clear();
}

bool wxVariantObjectSetData::Eq(wxVariantData& data) const
{
    wxASSERT_MSG( (data.GetType() == wxT("objectset")), wxT("wxVariantObjectSetData::Eq: argument mismatch") );

    if (data.GetType() != wxT("objectset"))
        return false;

    wxVariantObjectSetData& setData = (wxVariantObjectSetData&) data;

    // TODO: Just compare count right now
    return m_objects.GetCount() == setData.m_objects.GetCount();
}

bool wxVariantObjectSetData::Write(wxString& WXUNUSED(str)) const
{
    return false;
}

bool wxVariantObjectSetData::Read(wxString& WXUNUSED(str))
{
    wxFAIL_MSG(wxT("Unimplemented"));
    // TODO
    return false;
}

/// Delete image
bool wxVariantObjectSetData::DeleteObject(size_t i)
{
    if (i >= 0 && i < m_objects.GetCount())
    {
        wxNode* node = m_objects.Item(i);
        wxVariant* obj = (wxVariant*) node->GetData();
        delete obj;
        delete node;

        return true;
    }
    else
        return false;
}

/// Get data
wxVariant* wxVariantObjectSetData::GetObject(size_t i) const
{
    wxVariant* data = (wxVariant*) m_objects.Item(i)->GetData();
    if (data)
    {
        wxString type = data->GetType();
    }
    return data;
}

/// Find object by name
wxVariant* wxVariantObjectSetData::FindObject(const wxString& field, const wxString& name) const
{
    size_t i;
    for (i = 0; i < GetCount(); i++)
    {
        wxVariant* obj = GetObject(i);
        if (obj)
        {
            if (wxVariantFindPropertyString(*obj, field) == name)
                return obj;
        }
    }
    return NULL;
}


/// Add data
void wxVariantObjectSetData::AddObject(wxVariant* data)
{
    wxASSERT( data->GetType() == wxT("list") );

    m_objects.Append(data);
}

// Is this variant an object set?
bool wxIsVariantObjectSetData(const wxVariant& variant)
{
    return (variant.GetType() == wxT("objectset"));
}

// Get the object set data
wxVariantObjectSetData* wxGetVariantObjectSetData(const wxVariant& variant)
{
    if (!variant.GetData())
        return NULL;

    wxASSERT( (variant.GetType() == wxT("objectset")));

    if (variant.GetType() == wxT("objectset"))
        return wxDynamicCast(variant.GetData(), wxVariantObjectSetData);
    else
        return NULL;
}

// Create object set
wxVariant wxCreateVariantObjectSetVariant()
{
    return wxVariant(new wxVariantObjectSetData);
}

/// Set property
void wxVariantSetProperty(wxVariant& var, const wxString& name, const wxVariant& value)
{
    size_t i;
    for (i = 0; i < (size_t) var.GetCount(); i++)
    {
        wxVariant* var2 = (wxVariant*) var.GetList().Item(i)->GetData();
        if (var2 && (var2->GetName() == name))
        {
            (*var2) = value;
            return;
        }
    }

    wxVariant var2(value);
    var2.SetName(name);

    var.Append(var2);
}

/// Set property as string
void wxVariantSetProperty(wxVariant& var, const wxString& name, const wxString& value)
{
    wxVariantSetProperty(var, name, wxVariant(value, name));
}

/// Set property
void wxVariantSetProperty(wxVariant& var, int i, const wxVariant& value)
{
    wxVariant* prop = wxVariantGetPropertyPtr(var, i);
    if (prop)
    {
        wxVariant var2(value);
        var2.SetName(prop->GetName());
        (*prop) = var2;
    }
}

/// Set property as string
void wxVariantSetProperty(wxVariant& var, int i, const wxString& value)
{
    wxVariantSetProperty(var, i, wxVariant(value, wxEmptyString));
}

/// Find property as variant
wxVariant* wxVariantFindPropertyPtr(const wxVariant& var, const wxString& name)
{
    size_t i;
    for (i = 0; i < (size_t) var.GetCount(); i++)
    {
        wxVariant* var2 = (wxVariant*) var.GetList().Item(i)->GetData();
        if (var2 && (var2->GetName() == name))
        {
            return var2;
        }
    }
    return NULL;
}

/// Find property as variant
wxVariant wxVariantFindProperty(const wxVariant& var, const wxString& name)
{
    wxVariant* var2 = wxVariantFindPropertyPtr(var, name);
    if (var2)
        return *var2;
    else
        return wxVariant();
}

/// Find property as string
wxString wxVariantFindPropertyString(const wxVariant& var, const wxString& name)
{
    wxVariant var2 = wxVariantFindProperty(var, name);
    return var2.GetString();
}

/// Get property as variant
wxVariant wxVariantGetProperty(const wxVariant& var, int i)
{
    if (i > -1 && i < (int) var.GetCount())
        return (* (wxVariant*) var.GetList().Item(i)->GetData());
    else
        return wxVariant();
}

/// Get property as variant
wxVariant* wxVariantGetPropertyPtr(const wxVariant& var, int i)
{
    if (i > -1 && i < (int) var.GetCount())
        return (wxVariant*) var.GetList().Item(i)->GetData();
    else
        return NULL;
}

/*!
 * wxVariantObjectSetAccessor: a class to make it easy to
 * manipulate an "objectset" wxVariant
 */

/// Is this an object set?
bool wxVariantObjectSetAccessor::IsValid() const
{
    wxVariantObjectSetData* data = GetData();
    return data != NULL;
}

/// If not already an object set (could be null), make sure it is.
bool wxVariantObjectSetAccessor::MakeObjectSet()
{
    if (m_objectSet->IsNull())
    {
        m_objectSet->SetData(new wxVariantObjectSetData);
    }
    return true;
}

/// Access the nth variant object in the set
wxVariant* wxVariantObjectSetAccessor::GetVariant(int n)
{
    wxVariantObjectSetData* data = GetData();
    if (data)
    {
        return data->GetObject(n);
    }
    else
        return NULL;
}

wxVariant wxVariantObjectSetAccessor::GetVariant(int n) const
{
    wxVariantObjectSetData* data = GetData();
    if (data)
    {
        wxVariant* obj = data->GetObject(n);
        if (obj)
            return *obj;
    }
    return wxNullVariant;
}

/// Set the nth variant object
void wxVariantObjectSetAccessor::SetVariant(int n, const wxVariant& variant)
{
    AllocExclusive();
    wxVariantObjectSetData* data = GetData();
    if (data)
    {
        wxVariant* obj = data->GetObject(n);
        if (obj)
        {
            (*obj) = variant;
        }
    }
}

/// Find the variant object by property name/value pair (e.g. Name, Julian)
wxVariant* wxVariantObjectSetAccessor::FindVariant(const wxString& property, const wxVariant& value)
{
    return FindVariant(property, value.GetString());
}

wxVariant* wxVariantObjectSetAccessor::FindVariant(const wxString& property, const wxString& value)
{
    wxVariantObjectSetData* data = GetData();
    if (data)
    {
        wxVariant* obj = data->FindObject(property, value);
        return obj;
    }
    return NULL;
}

/// Find the variant index by pointer
int wxVariantObjectSetAccessor::FindVariantIndex(wxVariant* var)
{
    int i;
    for (i = 0; i < (int) var->GetCount(); i++)
    {
        wxVariant* v = GetVariant(i);
        if (v == var)
            return i;
    }
    return -1;
}

/// Add a variant
void wxVariantObjectSetAccessor::AddVariant(wxVariant* variant)
{
    AllocExclusive();
    wxVariantObjectSetData* data = GetData();
    if (data)
        data->AddObject(variant);
}

/// Insert a variant at the given position
void wxVariantObjectSetAccessor::InsertVariant(wxVariant* variant, int pos)
{
    // TODO
    AllocExclusive();
    wxVariantObjectSetData* data = GetData();
    if (data)
        data->AddObject(variant);
}

/// Delete a variant at the given position
bool wxVariantObjectSetAccessor::DeleteVariant(int pos)
{
    AllocExclusive();
    wxVariantObjectSetData* data = GetData();
    if (data)
        return data->DeleteObject(pos);
    else
        return false;
}

#if 0
/// Delete a variant by name/value
bool wxVariantObjectSetAccessor::DeleteVariant(const wxString& property, const wxVariant& value)
{
    AllocExclusive();
}

bool wxVariantObjectSetAccessor::DeleteVariant(const wxString& property, const wxString& value)
{
    AllocExclusive();
}
#endif

/// Get the property for the given variant object
wxVariant* wxVariantObjectSetAccessor::GetProperty(int var, int property)
{
    wxVariant* obj = GetVariant(var);
    if (obj)
    {
        return wxVariantGetPropertyPtr(*obj, property);
    }
    return NULL;
}

/// Find the named property in the given variant object
wxVariant* wxVariantObjectSetAccessor::FindProperty(int var, const wxString& propertyName)
{
    wxVariant* obj = GetVariant(var);
    if (obj)
    {
        return wxVariantFindPropertyPtr(*obj, propertyName);
    }
    return NULL;
}

/// Find the index of the property in the given variant object
int wxVariantObjectSetAccessor::GetPropertyIndex(int var, const wxString& propertyName)
{
    wxVariant* obj = GetVariant(var);
    if (obj)
    {
        wxVariant* p = wxVariantFindPropertyPtr(*obj, propertyName);
        if (!p)
            return -1;

        int i;
        for (i = 0; i < (int) p->GetCount(); i++)
        {
            wxVariant* v = wxVariantGetPropertyPtr(*p, i);
            if (v == p)
                return i;
        }
    }
    return -1;
}

/// Set the named property in the given variant object
bool wxVariantObjectSetAccessor::SetProperty(int var, const wxString& propertyName, const wxVariant& value)
{
    AllocExclusive();
    wxVariant* obj = GetVariant(var);
    if (obj)
    {
        wxVariantSetProperty(*obj, propertyName, value);
        return true;
    }
    else
        return false;
}

bool wxVariantObjectSetAccessor::SetProperty(int var, const wxString& propertyName, const wxString& value)
{
    return SetProperty(var, propertyName, wxVariant(value, propertyName));
}

bool wxVariantObjectSetAccessor::SetProperty(int var, int property, const wxVariant& value)
{
    AllocExclusive();
    wxVariant* p = GetProperty(var, property);
    if (p)
    {
        (*p) = value;
        return true;
    }
    else
        return false;
}

bool wxVariantObjectSetAccessor::SetProperty(int var, int property, const wxString& value)
{
    AllocExclusive();
    wxVariant* p = GetProperty(var, property);
    if (p)
    {
        (*p) = wxVariant(value, p->GetName());
        return true;
    }
    else
        return false;
}

/// Set the property image
bool wxVariantObjectSetAccessor::SetPropertyImage(int var, int property, const wxImage& image, int type)
{
    AllocExclusive();
    wxVariant* p = GetProperty(var, property);
    if (p)
    {
        if (p->GetType() == wxT("image"))
        {
            wxPictureVariantData* data = wxGetImageVariantData(*p);
            if (data)
            {
                data->SetImage(image, type);
                return true;
            }
        }
    }
    return false;
}

bool wxVariantObjectSetAccessor::SetPropertyImage(int var, int property, const wxString& filename, int type)
{
    AllocExclusive();
    wxVariant* p = GetProperty(var, property);
    if (p)
    {
        if (p->GetType() == wxT("image"))
        {
            wxPictureVariantData* data = wxGetImageVariantData(*p);
            if (data)
            {
                data->SetImage(filename, type);
                return true;
            }
        }
    }
    return false;
}

bool wxVariantObjectSetAccessor::SetPropertyImage(int var, const wxString& propertyName, const wxImage& image, int type)
{
    AllocExclusive();
    wxVariant* obj = GetVariant(var);
    if (obj)
    {
        wxVariant* p = FindProperty(var, propertyName);
        if (!p)
        {
            obj->Append(wxVariant(new wxPictureVariantData, propertyName));
            p = FindProperty(var, propertyName);
        }
        if (p)
        {
            wxPictureVariantData* data = wxGetImageVariantData(*p);
            if (data)
            {
                data->SetImage(image, type);
                return true;
            }
        }
    }
    return false;
}

bool wxVariantObjectSetAccessor::SetPropertyImage(int var, const wxString& propertyName, const wxString& filename, int type)
{
    AllocExclusive();
    wxVariant* obj = GetVariant(var);
    if (obj)
    {
        wxVariant* p = FindProperty(var, propertyName);
        if (!p)
        {
            obj->Append(wxVariant(new wxPictureVariantData, propertyName));
            p = FindProperty(var, propertyName);
        }
        if (p)
        {
            wxPictureVariantData* data = wxGetImageVariantData(*p);
            if (data)
            {
                data->SetImage(filename, type);
                return true;
            }
        }
    }
    return false;
}

/// Get the image, if any
wxImage* wxVariantObjectSetAccessor::GetPropertyImage(int var, int property)
{
    wxVariant* p = GetProperty(var, property);
    if (p)
    {
        if (p->GetType() == wxT("image"))
        {
            wxPictureVariantData* data = wxGetImageVariantData(*p);
            if (data)
                return & data->GetImage();
        }
    }
    return NULL;
}

wxImage* wxVariantObjectSetAccessor::FindPropertyImage(int var, const wxString& propertyName)
{
    wxVariant* p = FindProperty(var, propertyName);
    if (p)
    {
        if (p->GetType() == wxT("image"))
        {
            wxPictureVariantData* data = wxGetImageVariantData(*p);
            if (data)
                return & data->GetImage();
        }
    }
    return NULL;
}

/// Get the image block
wxImageBlock* wxVariantObjectSetAccessor::GetPropertyImageBlock(int var, int property)
{
    wxVariant* p = GetProperty(var, property);
    if (p)
    {
        if (p->GetType() == wxT("image"))
        {
            wxPictureVariantData* data = wxGetImageVariantData(*p);
            if (data)
                return & data->GetImageBlock();
        }
    }
    return NULL;
}

wxImageBlock* wxVariantObjectSetAccessor::FindPropertyImageBlock(int var, const wxString& propertyName)
{
    wxVariant* p = FindProperty(var, propertyName);
    if (p)
    {
        if (p->GetType() == wxT("image"))
        {
            wxPictureVariantData* data = wxGetImageVariantData(*p);
            if (data)
                return & data->GetImageBlock();
        }
    }
    return NULL;
}

/// Ensure image has been created from the data
bool wxVariantObjectSetAccessor::MakePropertyImage(int var, int property, bool force)
{
    wxVariant* p = GetProperty(var, property);
    if (p)
    {
        if (p->GetType() == wxT("image"))
        {
            wxPictureVariantData* data = wxGetImageVariantData(*p);
            if (data)
                return data->MakeImage(force);
        }
    }
    return false;
}

bool wxVariantObjectSetAccessor::MakePropertyImage(int var, const wxString& propertyName, bool force)
{
    wxVariant* p = FindProperty(var, propertyName);
    if (p)
    {
        if (p->GetType() == wxT("image"))
        {
            wxPictureVariantData* data = wxGetImageVariantData(*p);
            if (data)
                return data->MakeImage(force);
        }
    }
    return false;
}


/// Make a copy of the object set
wxVariant wxVariantObjectSetAccessor::Copy() const
{
    return *m_objectSet;
}

/// Get the object set count
int wxVariantObjectSetAccessor::GetCount() const
{
    wxVariantObjectSetData* data = GetData();
    if (data)
        return data->GetCount();
    return 0;
}

/// Get the ref data (containing the real data)
wxVariantObjectSetData* wxVariantObjectSetAccessor::GetData() const
{
    if (m_objectSet)
    {
        wxVariantObjectSetData* data = wxDynamicCast(m_objectSet->GetData(), wxVariantObjectSetData);
        return data;
    }
    return NULL;
}

/// Make sure we're the exclusive owner of the data
void wxVariantObjectSetAccessor::AllocExclusive() const
{
    if (m_objectSet)
    {
        wxVariantObjectSetData* data = wxDynamicCast(m_objectSet->GetData(), wxVariantObjectSetData);
        if (!data)
        {
            m_objectSet->SetData(new wxVariantObjectSetData);
            data = wxDynamicCast(m_objectSet->GetData(), wxVariantObjectSetData);
        }
        wxVariantAllocExclusive(*m_objectSet);
    }
}

/// Clear the set
bool wxVariantObjectSetAccessor::Clear()
{
    wxVariantObjectSetData* data = GetData();
    if (data)
        data->Clear();
    return true;
}
/*!
 * wxObjectSetVariantHandler knows how to create, load and save an image variant.
 */

class wxObjectSetVariantHandler: public wxVariantHandler
{
    DECLARE_DYNAMIC_CLASS(wxObjectSetVariantHandler)
public:
    wxObjectSetVariantHandler(const wxString& name = wxEmptyString)
        : wxVariantHandler(name)
        { }

    virtual bool ReadValue(const wxString& type, wxVariant& variant, wxInputStream& stream, wxDataInputStream& dataStream);
    virtual bool WriteValue(const wxVariant& variant, wxOutputStream& stream, wxDataOutputStream& dataStream);

    virtual bool CanHandle(const wxString& type);

    /// Supply a default value for this type
    virtual wxVariant GetDefaultValue(const wxString& type) { return wxCreateVariantObjectSetVariant(); }
};

IMPLEMENT_DYNAMIC_CLASS(wxObjectSetVariantHandler, wxVariantHandler)

/// Read the variant data from a stream
bool wxObjectSetVariantHandler::ReadValue(const wxString& type, wxVariant& variant, wxInputStream& stream, wxDataInputStream& dataStream)
{
    if (type == wxT("objectset"))
    {
        wxVariantObjectSetAccessor objectSet(& variant);

        objectSet.MakeObjectSet();
        objectSet.AllocExclusive();
        objectSet.Clear();

        int n = dataStream.Read16();

        int i;
        for (i = 0; i < n; i++)
        {
            wxVariant *var = new wxVariant;
            wxVariantHandlers::Read(*var, stream, dataStream);
            objectSet.AddVariant(var);
        }

        return true;
    }
    else
        return false;
}

/// Write the variant data to the stream
bool wxObjectSetVariantHandler::WriteValue(const wxVariant& variant, wxOutputStream& stream, wxDataOutputStream& dataStream)
{
    wxString name(variant.GetName());
    // wxASSERT( !name.IsEmpty() );

    wxString type(variant.GetType());
    wxASSERT( !type.IsEmpty() );

    if (type == wxT("objectset"))
    {
        wxVariantObjectSetAccessor objectSet(& variant);

        int n = objectSet.GetCount();
        dataStream.Write16(n);

        int i;
        for (i = 0; i < n; i++)
        {
            wxVariant* var = objectSet.GetVariant(i);

            wxVariantHandlers::Write(*var, stream, dataStream);
        }

        return true;
    }
    else
        return false;
}

bool wxObjectSetVariantHandler::CanHandle(const wxString& type)
{
    return type == wxT("objectset");
}

// A module to register wxObjectSetVariantHandler

class wxObjectSetVariantHandlerModule: public wxModule
{
DECLARE_DYNAMIC_CLASS(wxObjectSetVariantHandlerModule)
public:
    wxObjectSetVariantHandlerModule() {}
    bool OnInit() { wxVariantHandlers::AddHandler(new wxObjectSetVariantHandler()); return true; };
    void OnExit() {}
};

IMPLEMENT_DYNAMIC_CLASS(wxObjectSetVariantHandlerModule, wxModule)

